#-*-makefile-*-

all: build

TOPLVL = ../..

include $(TOPLVL)/projects/common/Makefile.config
include $(TOPLVL)/projects/common/Makefile.shared

SHELL  = /bin/bash

CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)

SPECFILE = $(addsuffix .spec, $(PKG_NAME))

SRPMVERS = $(shell [ ! -f $(SPECFILE) ] || $(call queryspec,%{NVR}\n,$(SPECFILE)))
SRPMFILE = results/$(SRPMVERS).src.rpm

LATEST_RPMS = $(wildcard rpms/*.rpm)
DEBUGINFO_RPM = $(wildcard rpms/${PKG_NAME}-debuginfo-$(shell $(call queryspec,%{V}-%{R},${SPECFILE})).*.rpm)
RPMS ?= $(LATEST_RPMS)

WITH_SUDO = sudo

# MOCK_CONFIG_VAL is set in Makefile.shared
MOCK_SMP_FLAGS=$(patsubst -j%, -D_smp_mflags\ -j%,$(filter -j%, $(MAKEFLAGS)))
MOCK ?= $(WITH_SUDO) /usr/bin/mock -r $(MOCK_CONFIG_VAL) $(MOCK_SMP_FLAGS) $(MOCK_OPTS)

MOCK_CLEANUP_OPT := $(if $(CLEANUP),--cleanup-after,--no-cleanup-after)
AUTOSPEC_CLEANUP_OPT := $(if $(CLEANUP),-C)

ifneq ($(wildcard upstream),)
__allsources := $(notdir $(strip $(shell cat upstream)))
__missingsources := $(filter-out $(wildcard ${__allsources}), ${__allsources})
endif

$(SPECFILE):
	@[[ -f "$@" ]] || (\
	  echo Error: $@ is missing. Please run \`make autospec\` to try and auto;\
	  echo Error: generate one for you.;\
	  exit 1 )

upstream:
	@[[ -f "$@" ]] || (\
	  echo Error: The \"$@\" file is missing. Try running \`make autospec\` to;\
	  echo Error: auto generate one for you. If you are not using autospec to;\
	  echo Error: generate a spec file, specify the proper "Source*" entries in the;\
	  echo Error: spec header, and try running \`make generateupstream\`.;\
	  exit 1 )

#help spdxcheck: Checks that the package license is a valid SPDX license,
#help or that it is in the exceptions list.
spdxcheck: $(SPECFILE)
	@for LICENSE in `$(call queryspec,%{LICENSE}\n,$(SPECFILE))`; do \
		if ! grep -qx "$$LICENSE" $(TOPLVL)/projects/common/licenses-{spdx,exceptions,extra}; then FAIL=yes; echo "ERROR: License '$$LICENSE' is not an allowed SPDX license ID"; fi; \
	done; test -z "$$FAIL"

#help clean: Cleans up the src.rpm file. but not the rpm files or log files.
clean:
	rm -f $(SRPMFILE)

#help proper: Brings your copy of the package to a nearly clean git checkout state.
proper: repodel
	rm -rf results rpms
	rm -f build.log.round*
	rm -f mock_build.log mock_srpm.log $(__allsources) report.html

#help mockclean: Clean all mock chroots and cache directories for all packages.
mockclean:
	$(MOCK) --clean --scrub=all

#help mockproper: Clean mock chroot
mockproper:
	$(MOCK) --clean --uniqueext=$(PKG_NAME)

configemail:
	@test -d .git || git init -b main
	@$(call subjectprefix,$(PKG_NAME))
	@echo -n "Subject prefix email configured to: "
	@git config --get format.subjectPrefix

#help checkblacklist: Verify rpms files to not include banned files for Clear Linux OS.
checkblacklist:
	@$(TOPLVL)/projects/common/checkblacklist.sh $(TOPLVL)/projects/common/blacklist $(filter-out ${DEBUGINFO_RPM},${RPMS})

prebuild-checks:

#help build: Builds from current directory on your machine and puts log
#help files and rpms in the results directory of your package.  If there
#help are mutlple tar.gz files for a package, make will do make sources,
#help which creates a src.rpm file from the spec file.
#help Use MOCK_OPTS environment varible to pass down arbitrary mock options.
build: prebuild-checks configemail $(SPECFILE) upstream $(SRPMFILE)
	$(MOCK) $(SRPMFILE) --result=results/ $(MOCK_CLEANUP_OPT) --uniqueext=$(PKG_NAME)
	$(MAKE) link-new-rpms PKG_REPO_DIR="."
	@perl $(TOPLVL)/projects/common/logcheck.pl results/build.log
	@perl $(TOPLVL)/projects/common/count.pl results/build.log &> testresults
	@$(MAKE) checkblacklist

#help build-nocheck: Same as 'make build', but do not run the package's test suite.
build-nocheck: configemail $(SPECFILE) upstream $(SRPMFILE)
	$(MOCK) $(SRPMFILE) --nocheck --result=results/ $(MOCK_CLEANUP_OPT) --uniqueext=$(PKG_NAME)
	$(MAKE) link-new-rpms PKG_REPO_DIR="."
	@perl $(TOPLVL)/projects/common/logcheck.pl results/build.log
	@perl $(TOPLVL)/projects/common/count.pl results/build.log &> testresults
	@$(MAKE) checkblacklist

fmvpatches: results/build.log
	@perl $(TOPLVL)/projects/common/fmvpatches.pl $(PKG_NAME)

#help rootshell: Puts you in a root shell at the top of your build root.
rootshell:
	$(MOCK) --result=results/ --no-cleanup-after --shell --uniqueext=$(PKG_NAME)

#help shell: Puts you in a user shell in the home directory in the build root.
shell:
	$(MOCK) --result=results/ --no-cleanup-after --shell --uniqueext=$(PKG_NAME) --unpriv "cd && exec \$$SHELL -l -i"

# Always rebuild the source rpm
.PHONY: $(SRPMFILE)
$(SRPMFILE): $(SPECFILE) $(__allsources) localreponotice clean-old-content
	@$(MAKE) spdxcheck
	$(MOCK) --buildsrpm --source=./ --spec=$(SPECFILE) --result=results/ $(MOCK_CLEANUP_OPT) --uniqueext=$(PKG_NAME)
	mv results/root.log results/srpm-root.log
	mv results/build.log results/srpm-build.log

# Do a git fetch and a git rebase to apply local commits on top of latest
# commits from the remote. A git fetch/rebase is used rather than a git pull so
# aborting the git pull at a password prompt doesn't leave local changes
# stashed. Do the git status dance so we only apply a stash we create. Note
# that a fetch/rebase is not attempted if the remote 'origin' is not
# initialized locally, or if the remote contains no commits.
pullrebase:
	@if [ -z "$$SKIP_GIT" ] && [ -e .git ]; then \
		if ! git rev-parse --verify --quiet origin/main > /dev/null; then \
			echo "Remote not yet initialized. Continuing."; \
			exit 0; \
		fi; \
		echo "Fetching origin"; \
		git fetch; \
		if ! git rev-parse --verify --quiet FETCH_HEAD > /dev/null; then \
			echo "No commits exist in remote. Continuing."; \
			exit 0; \
		fi; \
		if [ "$$(git rev-list FETCH_HEAD | head -n 1)" = "$$(git rev-list HEAD | head -n 1)" ]; then \
			echo "Current branch up-to-date. Continuing."; \
			exit 0; \
		fi; \
		echo "Rebasing to origin/main"; \
		if git status --porcelain | grep -q '^.[^?]'; \
		then \
			git stash save; \
			git rebase FETCH_HEAD; \
			git stash apply; \
		else \
			git rebase FETCH_HEAD; \
		fi \
	fi

preautospec-checks:

#help autospec: automatically generates a specfile. If there is
#help already a specfile, it will be overwritten. Several files used by
#help autospec will be created in the process.
#help Use MOCK_OPTS environment varible to pass down arbitrary mock options
#help to autospec.
#help For more information, see the project at https://github.com/clearlinux/autospec
autospec: preautospec-checks pullrebase localreponotice clean-old-content
	git rm --ignore-unmatch pumpAutospec || rm -f pumpAutospec
	@if [ -e $(SPECFILE) ] && ! grep -q "# Generated by: autospec.py" $(SPECFILE) ; then \
		echo "Specfile already exists and was not created by autospec.py! Aborting."; \
		exit 1; \
	fi
	$(eval ARCHIVES = $(shell $(TOPLVL)/projects/common/vendor.py '$(value ARCHIVES)' $(firstword $(NEWURL) $(URL)) $(PKG_NAME) $(VND_BASE_URL)))
	@printf 'PKG_NAME := %s\nURL = %s\nARCHIVES = %s\n\ninclude ../common/Makefile.common\n' $(PKG_NAME) '$(firstword $(value NEWURL) $(value URL))' '$(value ARCHIVES)' > Makefile
	python3 $(TOPLVL)/projects/autospec/autospec/autospec.py \
		--target . \
		--integrity \
		--config "$(AUTOSPEC_CONF)" \
		--name $(PKG_NAME) \
		--archives $(ARCHIVES) \
		--mock-config $(MOCK_CONFIG_VAL) \
		--mock-opts="$(MOCK_SMP_FLAGS) $(MOCK_OPTS)" \
		$${SETVERSION:+ --version $${SETVERSION}} \
		$${NON_INTERACTIVE:+ --non_interactive} \
		$${SKIP_GIT:+ --skip-git} \
		$(AUTOSPEC_CLEANUP_OPT) \
		$(firstword $(NEWURL) $(URL));
	$(MAKE) link-new-rpms PKG_REPO_DIR="."
	@$(MAKE) spdxcheck
	@$(MAKE) checkblacklist
	@if [ -e update_changelog.sh ] && [ -z "$$SKIP_GIT" ] ; then \
		bash update_changelog.sh ; \
		git commit --amend --no-edit Change* ; \
	fi
	@$(MAKE) -s for-review.txt
	diffstat for-review.txt
	@printf "\n**\n"
	@printf "** NOTICE: A patch with changes is available in the file for-review.txt\n"
	@printf "** To recreate (e.g., after git commit --amend), run make for-review.txt\n"
	@printf "** To submit for review: git send-email --to <recipient> for-review.txt\n"
	@printf "**\n\n"

#help for-review.txt: Creates the for-review.txt file, which is
#help a minimized version of the Git commit, suitable for code review.
for-review.txt:
	git diff | grep -q index || python3 $(TOPLVL)/projects/common/patchfilter.py > for-review.txt
.PHONY: for-review.txt

#help autospecnogit: Runs autospec, but does not create a commit
autospecnogit:
	$(MAKE) autospec SKIP_GIT=1

#help autospecnostate: Runs autospec, but cleans up mock chroots
#help and disables interactive mode.
autospecnostate:
	$(MAKE) autospec CLEANUP=1 NON_INTERACTIVE=1

scanlicense:
	python3 $(TOPLVL)/projects/autospec/autospec/autospec.py -t . --config $(AUTOSPEC_CONF) --license-only $(firstword $(NEWURL) $(URL)) --name $(PKG_NAME)

#help bump: Increments the package release number by one and commits the result. If
#help the variable BUMP_MSG is set, its value is used as the commit summary.
#help Otherwise a generic commit summary is used.
bump:
	git stash
	git pull --rebase
	$(MAKE) bumpnogit
	git add $(SPECFILE) release
	fromver=$(shell $(call queryspec,%{VERSION}-%{RELEASE}\n,$(SPECFILE))); \
	tover=`$(call queryspec,%{VERSION}-%{RELEASE}\n,$(SPECFILE))`; \
	if [[ -n "$(BUMP_MSG)" ]]; then \
		bumpmsg="$(BUMP_MSG)"; \
	else \
		bumpmsg="version bump from $$fromver to $$tover"; \
	fi; \
	git commit -a -m "$$bumpmsg"

#help bumpnogit: Increments the package release number by one and does not commit
#help changes.
bumpnogit:
	@$(MAKE) spdxcheck
	oldrel=$(shell $(call queryspec,%{RELEASE}\n,$(SPECFILE))); \
	newrel=$$(($$oldrel + 1)); \
	sed -i "s/^\(Release[[:blank:]]*:[[:blank:]]*\)$$oldrel$$/\1$$newrel/" $(SPECFILE); \
	echo "$$newrel" > release;

$(__missingsources): sources

# First argument is a Clear Linux build number (e.g. 22000)
define fetch-from-srpm
	tmp=$$(mktemp -d -p "."); \
	srpm=$$($(call queryspec,%{NVR}.src.rpm\n,$(SPECFILE))); \
	if cd "$$tmp" && ! curl -s -S -f -L -O $(MIRROR_CURL_OPTS) $(DOWNLOAD_MIRROR)/releases/$(1)/clear/source/SRPMS/$$srpm; then \
		echo "Failed to download $$srpm from Clear Linux release $(1)." >&2; \
		cd .. && rm -rf "$$tmp"; \
		exit 1; \
	fi; \
	rpm2cpio $$srpm | cpio --quiet -id; \
	cd ..; \
	srclist=$$($(call queryspec,[%{SOURCE}\n],$(SPECFILE))); \
	for s in $$srclist; do \
		if ! mv --no-clobber "$$tmp"/$$s .; then \
			echo "Missing source file \"$$s\" in $${srpm}." >&2; \
			rm -rf "$$tmp"; \
			exit 1; \
		fi; \
		echo "Retrieved source file $$s ..."; \
	done; \
	rm -rf "$$tmp"
endef

#help sources: If SOURCES_URL is defined, download required upstream source files
#help from that location. Otherwise, try to download source files according to the
#help URLs listed in the spec file. If any of the source files fail to download,
#help check for the relevant SRPM from published releases of Clear Linux OS, and
#help extract the files if found. Note that SRPMs are taken from the DOWNLOAD_MIRROR
#help location. This will run automatically, as a dependency. NOTE: A Koji server can
#help make use of this "make sources" command, since it lives in a repo named
#help "common". If you use this makefile in Koji, ensure "make sources" continues to
#help work for both remote and local builds, since it is a prerequisite of several
#help commands for building packages.
ifneq ($(strip $(SOURCES_URL)),)
sources: upstream
	while read u; do \
		case "$$u" in \
			"")    continue ;; \
			*://*) n="$$u" ;; \
			*)     n="$(SOURCES_URL)/$$u" ;; \
		esac; \
		if ! curl --fail -L -o `basename "$$n"` $(SOURCES_CURL_OPTS) "$$n"; then \
			echo "Failed to download $$n"; \
			exit 1; \
		fi; \
	done < upstream
else
sources:
	@$(MAKE) generateupstream; \
	[ $$? -eq 0 ] && exit 0; \
	nvr="$$($(call queryspec,%{NAME}\t%{VERSION}\t%{RELEASE}\n,$(SPECFILE)))"; \
	git -C $(TOPLVL)/projects/common fetch --tags >/dev/null 2>&1; \
	latest_builds=$$(git -C $(TOPLVL)/projects/common tag -l | sort -rn); \
	for b in $$latest_builds; do \
		url="$(DOWNLOAD_MIRROR)/releases/$$b/clear/source/package-sources"; \
		echo "Checking for source files in build $$b ..."; \
		if grep -q "$$nvr" <(curl -s -f -L $(MIRROR_CURL_OPTS) $$url); then \
			$(call fetch-from-srpm,$$b); \
			if [ $$? -eq 0 ]; then \
				echo "Source files retrieved from build $$b"; \
				exit 0; \
			fi; \
		fi; \
	done; \
	echo "Source files not found for package"; \
	exit 1
endif

prekoji-checks:

#help koji: Creates a git tag on the latest local commit, then asks koji to
#help build from that tag on the server. If you have uncommitted local changes,
#help they will not be included.
koji: prekoji-checks kojidef
	@$(MAKE) spdxcheck
	@$(MAKE) checkblacklist
	@if [ "$(CURRENT_BRANCH)" != "main" ]; then \
		echo "Error: Must be on the \"main\" branch to submit to koji" >&2; \
		exit 1; \
	fi; \
	if ! git diff --quiet HEAD -- ${SPECFILE}; then \
		echo "Error: All changes to ${SPECFILE} must be committed first" >&2; \
		exit 1; \
	fi; \
	git fetch; \
	if git rev-parse --verify --quiet origin/main >/dev/null; then \
		git pull --rebase; \
	fi; \
	if git ls-remote --tags --exit-code origin $(SRPMVERS) >/dev/null; then \
		echo "Error: remote tag $(SRPMVERS) already exists" >&2; \
		exit 1; \
	fi
	git tag $(SRPMVERS)
	git push origin $(CURRENT_BRANCH):main refs/tags/$(SRPMVERS)
	$(KOJI_CMD) build $$KOJI_NOWAIT $(KOJI_TAG) $(PKG_BASE_URL)/$(PKG_NAME)?#$(SRPMVERS)
	@if [ -f bump.list ]; then \
		$(MAKE) koji-waitrepo; \
		msg="Bump for $(shell $(call queryspec,%{NAME} %{VERSION}\n,$(SPECFILE)))"; \
		for bump_dep in $$(cat bump.list); do \
			(cd ../"$$bump_dep" && $(MAKE) bump BUMP_MSG="$$msg" && $(MAKE) koji-nowait) \
		done \
	fi

#help rekoji: In case a previous 'make koji' failed, trigger a rebuild for the
#help same tag.
rekoji: kojidef
	git pull --rebase
	$(KOJI_CMD) build $$KOJI_NOWAIT $(KOJI_TAG) $(PKG_BASE_URL)/$(PKG_NAME)?#$(SRPMVERS)

#help scratch: Performs scratch-build on package in the background.
scratch: $(SPECFILE) upstream $(SRPMFILE) kojidef
	$(KOJI_CMD) build --scratch --nowait --background $$KOJI_NOWAIT $(KOJI_TAG) $(SRPMFILE)

#help scratch-wait: Performs scratch-build on package.
scratch-wait: $(SPECFILE) upstream $(SRPMFILE) kojidef
	$(KOJI_CMD) build --scratch --wait $(KOJI_TAG) $(SRPMFILE)

#help koji-nowait: Same as 'make koji', but do not block
koji-nowait:
	$(MAKE) KOJI_NOWAIT="--nowait --background" koji &

#help rekoji-nowait: Same as 'make rekoji', but do not block
rekoji-nowait:
	$(MAKE) KOJI_NOWAIT="--nowait --background" rekoji

#help koji-waitrepo: Wait for current package changes to become available in the
#help koji repo. If PKG_NAME is overridden on the command line, wait on changes for
#help that package instead. This capability allows the developer to "chain" builds
#help for submission to koji according to the state of the package repos on their
#help system. Additional options for `koji wait-repo` can be specified via the
#help WAIT_OPTS variable.
koji-waitrepo: kojidef
	@if [[ ! "${PKG_NAME}" =~ ^[A-Za-z0-9._+-]+$$ ]]; then \
		echo "[ERROR] Invalid package name \"${PKG_NAME}\""; \
		exit 1; \
	fi; \
	if [[ "${PKG_NAME}" != "$(notdir ${CURDIR})" ]]; then \
		$(MAKE) -s -C ../${PKG_NAME} koji-waitrepo; \
	else \
		koji wait-repo --build=${SRPMVERS} ${WAIT_OPTS} ${KOJI_TAG}-build; \
	fi

.PHONY: koji-tag
#help koji-tag: runs koji tag-pkg on what's in the current specfile's nvr
koji-tag:
	koji tag-pkg dist-clear $(SRPMVERS)

.PHONY: koji-untag
#help koji-untag: runs koji untag-pkg on what's in the current specfile's nvr
koji-untag:
	koji untag-pkg dist-clear $(SRPMVERS)


update-versions:

.PHONY: update
#help update: Tries to run update.sh if it exists. update.sh is expected
#help to check for version updates, exit (successfully) if none are found
#help or update the package and push the update to koji.
#help If no update.sh exists, update the version, autospec and push the
#help update to koji (bumping the bump.list if found).
update: export AUTOSPEC_UPDATE=1
update:
	@if [ -f update.first ]; then \
		for pkg in $$(< update.first); do \
			(cd "../$${pkg}" || exit 1; $(MAKE) update-versions || exit 0; $(MAKE) update) || exit; \
		done; \
	fi
	@if [ -f update.sh ]; then \
		./update.sh; \
	else \
		$(MAKE) -s update-versions && \
		$(MAKE) autospec CLEANUP=1 && \
		$(MAKE) -s koji && \
		$(MAKE) koji-waitrepo; \
	fi \

logcheck:
	@perl $(TOPLVL)/projects/common/logcheck.pl results/build.log

#help repoadd: Adds locally-built RPMs for this package to the local RPM
#help repository. If this repository does not yet exist, it is created and
#help enabled.
repoadd: $(TOPLVL)/repo
	@if [ -z "${LATEST_RPMS}" ]; then \
		echo "No rpms found in rpms/ directory."; \
		exit 1; \
	fi
	$(MAKE) -s repodel NO_CREATEREPO=1
	@echo "Adding new rpms:"; \
	for rpm in ${LATEST_RPMS}; do \
		echo "+$${rpm#rpms/}"; \
		echo "$${rpm#rpms/}" >> .repo-index; \
	done; \
	flock $</repo.lock ln -f -t $< ${LATEST_RPMS}
	$(MAKE) -s localrepocreate
	$(MAKE) -s localrepoenable

#help repodel: Removes RPMs from the local RPM repository that were
#help previously added by 'make repoadd' for this package.
repodel: $(TOPLVL)/repo
	@if [ -f .repo-index ]; then \
		echo "Cleaning old rpms:"; \
		mapfile -t rpms < <(sed 's|.*|$</&|' .repo-index); \
		for rpm in "$${rpms[@]}"; do \
			echo "-$${rpm#$</}"; \
		done < .repo-index; \
		flock $</repo.lock rm -f "$${rpms[@]}"; \
		rm .repo-index; \
		if [ -z "$$NO_CREATEREPO" ]; then \
			$(MAKE) -s localrepocreate; \
		fi; \
	fi

#help repoenable: Enables the local RPM repository for use with Yum/DNF and
#help Mock. If this repository does not yet exist, it is created.
repoenable: localrepoenable ;

#help repodisable: Disables the local RPM repository.
repodisable: localrepodisable ;

#help repostatus: Summarizes the local RPM repository status.
repostatus: localrepostatus ;

loop-up:
	$(loopup)

loop-down:
	$(loopdown)

#help install: Install locally built RPMs for this package into an image
#help located at ../../clear.img. Make sure the image is not being used before
#help running this rule! Note that debuginfo RPMs are installed to the cache
#help directory used by the automatic debuginfo system in Clear Linux.
install:
	@$(MAKE) loop-up DEVICE=6 TARGET=$(TOPLVL)/clear.img
	topdir=$(TOPLVL)/image; \
	for r in $(RPMS); do rpm2cpio $$r | (cd $$topdir; sudo cpio -i -d -u); done; \
	for dir in lib src; do \
		if [ -d $$topdir/usr/$$dir/debug ]; then \
			find $$topdir/usr/$$dir/debug -type f -o -type l | while read path; do \
				newpath=`echo $$path | sed "s|\($$topdir\)/usr/$$dir/debug|\1/var/cache/debuginfo/$$dir|"`; \
				sudo mkdir -p `dirname $$newpath`; \
				sudo mv -f $$path $$newpath; \
			done; \
		fi; \
	done
	@$(MAKE) loop-down DEVICE=6

#help install-debuginfo-local: Install locally built debuginfo RPM to
#help the automatic debuginfo cache location (/var/cache/debuginfo)
install-debuginfo-local:
	@if [ -z "${DEBUGINFO_RPM}" ]; then \
		echo "No debuginfo to install... skipping"; \
		exit 0; \
	fi; \
	echo -n "Installing ${DEBUGINFO_RPM}... "; \
	tmpdir=$$(mktemp -d); \
	rpm2cpio ${DEBUGINFO_RPM} | ( cd $$tmpdir; cpio -i -d -u 2> /dev/null); \
	dest=/var/cache/debuginfo/lib; \
	find $$tmpdir/usr/lib/debug/ -mindepth 1 -maxdepth 1 2> /dev/null | while read -r d; do \
		sudo chown -R dbginfo:dbginfo "$$d"; \
		sudo cp -a "$$d" $$dest/; \
	done; \
	find $$tmpdir/usr/share/debug/.build-id -type l 2> /dev/null | while read -r link; do \
		new_target=$$(readlink $$link | sed 's|/usr/share/debug|/usr/lib/debug|'); \
		sudo ln -sf "$$new_target" "$$link"; \
	done; \
	find $$tmpdir/usr/share/debug/ -mindepth 1 -maxdepth 1 -regextype awk -regex '.*/(.build-id|boot|lib|sbin|usr)$$' 2> /dev/null | while read -r d; do \
		sudo chown -R dbginfo:dbginfo "$$d"; \
		sudo cp -a "$$d" $$dest/; \
	done; \
	dest=/var/cache/debuginfo/src; \
	find $$tmpdir/usr/src/debug/ -mindepth 1 -maxdepth 1 2> /dev/null | while read -r d; do \
		sudo chown -R dbginfo:dbginfo "$$d"; \
		sudo cp -a "$$d" $$dest/; \
	done; \
	find $$tmpdir/usr/share/debug/src/ -mindepth 1 -maxdepth 1 2> /dev/null | while read -r d; do \
		sudo chown -R dbginfo:dbginfo "$$d"; \
		sudo cp -a "$$d" $$dest/; \
	done; \
	sudo rm -rf $$tmpdir; \
	echo "done"

#help install-local: Install locally built RPMs to the root filesystem. Note that the
#help debuginfo RPM installs to /var/cache/debuginfo
install-local:
	@for r in $(filter-out ${DEBUGINFO_RPM},${RPMS}); do \
		echo -n "Installing $$r... "; \
		rpm2cpio $$r | (cd /; sudo cpio -i -d -u 2> /dev/null); \
		echo "done"; \
	done
	@$(MAKE) -s install-debuginfo-local

#help install-mock: Install locally built RPMs to the mock rootcache
#help filesystem. This command is usually used with "make shell".
install-mock: repoadd
	$(MOCK) --result=results/ --no-cleanup-after --uniqueext=$(PKG_NAME) -i $(filter-out ${DEBUGINFO_RPM},${RPMS})

#help generateupstream: Run this rule to create or update the 'upstream' file
#help by downloading the upstream source tarballs listed in the spec file and
#help calculating their hashes. Autospec performs this step automatically, so
#help ignore it for packages managed with autospec. Additional curl options
#help may be used for downloading the upstream source tarballs by defining the
#help CURL_OPTS variable for this rule.
generateupstream:
	@[ -e upstream ] && mv upstream upstream.bak; \
	urls=$$(rpmspec -D '_vendor clr' -P $(SPECFILE) | sed -n "s|^Source[0-9]*[[:blank:]]*:[[:blank:]]*\(..*://..*\)$$|\1|p"); \
	for url in $$urls; do \
		filename=$$(basename $$url); \
		if [ ! -e $$filename ]; then \
			if ! curl --fail -L -O $(CURL_OPTS) $$url; then \
				echo "Failed to download $$url"; \
				[ -e upstream.bak ] && mv --no-clobber upstream.bak upstream; \
				exit 1; \
			fi; \
		fi; \
		echo $$(sha1sum -- $$filename | cut -d\  -f1)/$$filename >> upstream; \
	done
	@rm -f upstream.bak
	@cat upstream

#help drop-abandoned: Remove all unused patches from the git tree
drop-abandoned: $(SPECFILE)
	python3 $(TOPLVL)/projects/common/drop-abandoned-patches.py $(SPECFILE)

#help cloc: Count lines of code with the `cloc` tool on the full sources of package
cloc: $(SRPMFILE)
	@$(MOCK) -r $(TOPLVL)/repo/clear.cfg $(SRPMFILE) --result=results/ --no-cleanup-after
	@$(MOCK) --chroot --copyin /usr/bin/cloc /usr/bin/cloc --result=results/ --no-cleanup-after
	@$(MOCK) --result=results/ --no-cleanup-after --chroot '/usr/bin/cloc /builddir > /tmp/cloc.txt'
	@$(MOCK) --copyout /tmp/cloc.txt results/ --result=results/
	@$(MOCK) --clean --scrub=chroot --uniqueext=$(PKG_NAME)
	cat results/cloc.txt

#help catchup: Backport the commits from the current version to the upstream HEAD (not release).
#help Only works if giturl is defined and the current package version can be mapped to a git tag.
catchup:
	$(MAKE) catchup-HEAD

#help catchup-<commit|tag>: Backport the commits from the current version to the specified commit or tag.
#help Only works if giturl is defined and the current package version can be mapped to a git tag.
catchup-%:
	@target=$*; \
	giturl=$$(grep -E '^giturl\s*=\s*\S+' options.conf | sed 's/giturl\s*=\s*//' 2>/dev/null); \
	if [[ -z "$${giturl}" ]]; then \
		echo "Error: giturl not defined in options.conf"; \
		exit 1; \
	fi; \
	mkdir -p results; \
	if [[ -d results/$(PKG_NAME) ]]; then \
		echo "Reusing existing repository..."; \
		git -C results/$(PKG_NAME) fetch origin; \
	else \
		echo "Cloning upstream repository..."; \
		git -C results clone "$${giturl}" $(PKG_NAME); \
	fi; \
	if ! git -C results/$(PKG_NAME) rev-parse --verify --quiet "$${target}" >/dev/null; then \
		echo "Error: Target commit/tag $${target} not found"; \
		exit 1; \
	fi; \
	version=$$(rpm -q --qf '%{VERSION}\n' --specfile $(SPECFILE) | head -1); \
	echo "Version: $${version}"; \
	current_tag=$$(git -C results/$(PKG_NAME) tag --list | grep -E "^($(PKG_NAME)-)?(.+-|v)?$${version}$$") || { \
		echo "Error: No tag found for current package version"; \
		exit 1; \
	}; \
	echo "Catching up from $${current_tag} to $${target}"; \
	for commit in $$(git -C results/$(PKG_NAME) log --reverse --pretty=oneline $${current_tag}..$${target} | cut -d' ' -f1); do \
		$(MAKE) backport-$${commit}; \
	done;


#help backport-<commit>: Retrieve a commit from the upstream git repository and save it as a backport patch.
#help The giturl is read from options.conf.
backport-%:
	@commit=$*; \
	echo "Backporting commit: $${commit}"; \
	giturl=$$(grep -E '^giturl\s*=\s*\S+' options.conf | sed 's/giturl\s*=\s*//' 2>/dev/null); \
	if [[ -z "$${giturl}" ]]; then \
		echo "Error: giturl not defined in options.conf"; \
		exit 1; \
	fi; \
	mkdir -p results; \
	if [[ -d results/$(PKG_NAME) ]]; then \
		echo "Reusing existing repository..."; \
		git -C results/$(PKG_NAME) fetch origin; \
	else \
		echo "Cloning upstream repository..."; \
		git -C results clone "$${giturl}" $(PKG_NAME); \
	fi; \
	full_commit=$$(git -C results/$(PKG_NAME) show --pretty=oneline $${commit} 2>/dev/null | head -1 | cut -d' ' -f 1); \
	if [[ -z "$${full_commit}" ]]; then \
		echo "Error: Commit for $${commit} not found"; \
		exit 1; \
	fi; \
	patch=backport-$${full_commit}.patch; \
	git -C results/$(PKG_NAME) format-patch -1 --stdout $${full_commit} > $${patch}; \
	if [[ -s $${patch} ]]; then \
		echo "$${patch} created"; \
		grep -qE "^$${patch}$$" series 2>/dev/null || echo "$${patch}" >> series; \
	else \
		rm -f $${patch}; \
		echo "Error: Failed to create backport patch"; \
		exit 1; \
	fi \

.PHONY: whatrequires
#help whatrequires: Output a list of packages that directly depend on this one,
#help showing the subpackage-level breakdown. Each line of output has the format
#help "SUBPACKAGE(|SYMBOL)? <- PACKAGE (ARCH)". If ARCH is "src", the meaning is
#help "PACKAGE has a BuildRequires (build dependency) on SUBPACKAGE". And if ARCH
#help is "x86_64", the meaning is "PACKAGE has a Requires (runtime dependency) on
#help SUBPACKAGE". The optional "|SYMBOL" portion is printed when the symbol
#help required does not match the subpackage name. Note that the ability to query
#help version-qualified dependencies is not yet implemented... (For example, if
#help python3-core provides the version-qualified symbol "python(abi) = 3.9",
#help running `make whatrequires` for `python3` will detect packages that depend on
#help "python(abi)", but not "python(abi) = 3.9".)
whatrequires:
	@Q="dnf --config=${PM_CONF} repoquery --quiet --releasever=clear"; \
	TMP=$$(mktemp -d); trap "rm -rf $$TMP" EXIT; \
	$${Q} --provides ${PKG_NAME} | awk '{ print $$1 }' > $$TMP/${PKG_NAME}; \
	( \
	while read -r provide; do \
		$${Q} --qf="${PKG_NAME} <- %{NAME} (x86_64)" --arch=x86_64 --srpm --whatrequires $$provide; \
		$${Q} --qf="${PKG_NAME} <- %{NAME} (src)" --arch=src --whatrequires $$provide; \
	done < $$TMP/${PKG_NAME} \
	) | awk '$$3 != "${PKG_NAME}"' | LC_COLLATE=C sort -u; \
	sed -n 's/^%package *\(.*\)$$/\1/p' ${PKG_NAME}.spec | sort > $$TMP/subpkgs; \
	while read -r val; do \
		if grep -qE '^-n +' <<< $$val; then \
			subpkg=$$(awk '{ print $$2 }' <<< $$val); \
		else \
			subpkg=${PKG_NAME}-$$val; \
		fi; \
		$${Q} --provides $$subpkg | awk '{ print $$1 }' > $$TMP/$$subpkg; \
		( \
		while read -r provide; do \
			$${Q} --qf="$$subpkg|$$provide <- %{NAME} (x86_64)" --arch=x86_64 --srpm --whatrequires $$provide; \
			$${Q} --qf="$$subpkg|$$provide <- %{NAME} (src)" --arch=src --whatrequires $$provide; \
		done < $$TMP/$$subpkg \
		) | awk '$$3 != "${PKG_NAME}"' | sed "s/^$$subpkg|\($$subpkg\)/\1/" | LC_COLLATE=C sort -u; \
	done < $$TMP/subpkgs

# Define LTS-specific targets in a separate makefile
-include $(TOPLVL)/projects/common/Makefile.common.lts

# Define site local common targets in a separate makefile
-include $(TOPLVL)/projects/common/Makefile.common.site_local

# Include optional extended makefiles from individual package repos
-include Makefile.custom
